/*
* SCR.java
* Copyright (C) 2003
* 
* $Id: SCR.java,v 1.18 2005/12/04 17:32:42 cawe Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using cvar_t = Suake2.UI.game.cvar_t;
using Suake2.UI.qcommon;
using S = Suake2.UI.sound.S;
using Sys = Suake2.UI.sys.Sys;
using Timer = Suake2.UI.sys.Timer;
using Lib = Suake2.UI.util.Lib;
using Vargs = Suake2.UI.util.Vargs;
//UPGRADE_TODO: The type 'java.nio.ByteBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteBuffer = java.nio.ByteBuffer;
//UPGRADE_TODO: The type 'java.nio.ByteOrder' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteOrder = java.nio.ByteOrder;
namespace Suake2.UI.client
{
	
	/// <summary> SCR</summary>
	public sealed class SCR:Globals
	{
		private class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.TimeRefresh_f();
			}
		}
		private class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.Loading_f();
			}
		}
		private class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.SizeUp_f();
			}
		}
		private class AnonymousClassxcommand_t3:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.SizeDown_f();
			}
		}
		private class AnonymousClassxcommand_t4:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.Sky_f();
			}
		}
		public class AnonymousClassxcommand_t5:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.SCR.UpdateScreen2();
			}
		}
		
		//	cl_scrn.c -- master for refresh, status bar, console, chat, notify, etc
		
		internal static System.String[][] sb_nums = new System.String[][]{new System.String[]{"num_0", "num_1", "num_2", "num_3", "num_4", "num_5", "num_6", "num_7", "num_8", "num_9", "num_minus"}, new System.String[]{"anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5", "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"}};
		
		/*
		* full screen console put up loading plaque blanked background with loading
		* plaque blanked background with menu cinematics full screen image for quit
		* and victory
		* 
		* end of unit intermissions
		*/
		
		internal static float scr_con_current; // aproaches scr_conlines at scr_conspeed
		
		internal static float scr_conlines; // 0.0 to 1.0 lines of console to display
		
		internal static bool scr_initialized; // ready to draw
		
		internal static int scr_draw_loading;
		
		// scr_vrect ist in Globals definiert
		// position of render window on screen
		
		internal static cvar_t scr_viewsize;
		
		internal static cvar_t scr_conspeed;
		
		internal static cvar_t scr_centertime;
		
		internal static cvar_t scr_showturtle;
		
		internal static cvar_t scr_showpause;
		
		internal static cvar_t scr_printspeed;
		
		internal static cvar_t scr_netgraph;
		
		internal static cvar_t scr_timegraph;
		
		internal static cvar_t scr_debuggraph;
		
		internal static cvar_t scr_graphheight;
		
		internal static cvar_t scr_graphscale;
		
		internal static cvar_t scr_graphshift;
		
		internal static cvar_t scr_drawall;
		
		public static cvar_t fps = new cvar_t();
		
		internal static dirty_t scr_dirty = new dirty_t();
		
		internal static dirty_t[] scr_old_dirty = new dirty_t[]{new dirty_t(), new dirty_t()};
		
		internal static System.String crosshair_pic;
		
		internal static int crosshair_width;
		internal static int crosshair_height;
		
		internal class dirty_t
		{
			internal int x1;
			
			internal int x2;
			
			internal int y1;
			
			internal int y2;
			
			internal void  set_Renamed(dirty_t src)
			{
				x1 = src.x1;
				x2 = src.x2;
				y1 = src.y1;
				y2 = src.y2;
			}
			
			internal void  clear()
			{
				x1 = x2 = y1 = y2 = 0;
			}
		}
		
		/*
		* ===============================================================================
		* 
		* BAR GRAPHS
		* 
		* ===============================================================================
		*/
		
		//	typedef struct
		//	{
		//		float value;
		//		int color;
		//	} graphsamp_t;
		internal class graphsamp_t
		{
			internal float value_Renamed;
			
			internal int color;
		}
		
		internal static int current;
		
		internal static graphsamp_t[] values = new graphsamp_t[1024];
		
		/*
		* ============== SCR_DebugGraph ==============
		*/
		public static void  DebugGraph(float value_Renamed, int color)
		{
			values[current & 1023].value_Renamed = value_Renamed;
			values[current & 1023].color = color;
			current++;
		}
		
		/*
		* ============== SCR_DrawDebugGraph ==============
		*/
		internal static void  DrawDebugGraph()
		{
			int a, x, y, w, i, h;
			float v;
			int color;
			
			// draw the graph
			
			w = scr_vrect.width;
			
			x = scr_vrect.x;
			y = scr_vrect.y + scr_vrect.height;
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			re.DrawFill(x, (int) (y - scr_graphheight.value_Renamed), w, (int) scr_graphheight.value_Renamed, 8);
			
			for (a = 0; a < w; a++)
			{
				i = (current - 1 - a + 1024) & 1023;
				v = values[i].value_Renamed;
				color = values[i].color;
				v = v * scr_graphscale.value_Renamed + scr_graphshift.value_Renamed;
				
				if (v < 0)
				{
					//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
					v += scr_graphheight.value_Renamed * (1 + (int) ((- v) / scr_graphheight.value_Renamed));
				}
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				h = (int) v % (int) scr_graphheight.value_Renamed;
				re.DrawFill(x + w - 1 - a, y - h, 1, h, color);
			}
		}
		
		/*
		* ===============================================================================
		* 
		* CENTER PRINTING
		* 
		* ===============================================================================
		*/
		
		// char scr_centerstring[1024];
		internal static System.String scr_centerstring;
		
		internal static float scr_centertime_start; // for slow victory printing
		
		internal static float scr_centertime_off;
		
		internal static int scr_center_lines;
		
		internal static int scr_erase_center;
		
		/*
		* ============== SCR_CenterPrint
		* 
		* Called for important messages that should stay in the center of the
		* screen for a few moments ==============
		*/
		internal static void  CenterPrint(System.String str)
		{
			//char *s;
			int s;
			System.Text.StringBuilder line = new System.Text.StringBuilder(64);
			int i, j, l;
			
			//strncpy (scr_centerstring, str, sizeof(scr_centerstring)-1);
			scr_centerstring = str;
			scr_centertime_off = scr_centertime.value_Renamed;
			scr_centertime_start = cl.time;
			
			// count the number of lines for centering
			scr_center_lines = 1;
			s = 0;
			while (s < str.Length)
			{
				if (str[s] == '\n')
					scr_center_lines++;
				s++;
			}
			
			// echo it to the console
			Com.Printf("\n\n\x001D\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001F\n\n");
			
			s = 0;
			
			if (str.Length != 0)
			{
				do 
				{
					// scan the width of the line
					
					for (l = 0; l < 40 && (l + s) < str.Length; l++)
						if (str[s + l] == '\n' || str[s + l] == 0)
							break;
					for (i = 0; i < (40 - l) / 2; i++)
						line.Append(' ');
					
					for (j = 0; j < l; j++)
					{
						line.Append(str[s + j]);
					}
					
					line.Append('\n');
					
					Com.Printf(line.ToString());
					
					while (s < str.Length && str[s] != '\n')
						s++;
					
					if (s == str.Length)
						break;
					s++; // skip the \n
				}
				while (true);
			}
			Com.Printf("\n\n\x001D\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001E\x001F\n\n");
			Console.ClearNotify();
		}
		
		internal static void  DrawCenterString()
		{
			System.String cs = scr_centerstring + "\x0000";
			int start;
			int l;
			int j;
			int x, y;
			int remaining;
			
			if (cs == null)
				return ;
			if (cs.Length == 0)
				return ;
			
			// the finale prints the characters one at a time
			remaining = 9999;
			
			scr_erase_center = 0;
			start = 0;
			
			if (scr_center_lines <= 4)
			{
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				y = (int) (viddef.height * 0.35);
			}
			else
				y = 48;
			
			do 
			{
				// scan the width of the line
				for (l = 0; l < 40; l++)
					if (start + l == cs.Length - 1 || cs[start + l] == '\n')
						break;
				x = (viddef.width - l * 8) / 2;
				SCR.AddDirtyPoint(x, y);
				for (j = 0; j < l; j++, x += 8)
				{
					re.DrawChar(x, y, cs[start + j]);
					if (remaining == 0)
						return ;
					remaining--;
				}
				SCR.AddDirtyPoint(x, y + 8);
				
				y += 8;
				
				while (start < cs.Length && cs[start] != '\n')
					start++;
				
				if (start == cs.Length)
					break;
				start++; // skip the \n
			}
			while (true);
		}
		
		internal static void  CheckDrawCenterString()
		{
			scr_centertime_off -= cls.frametime;
			
			if (scr_centertime_off <= 0)
				return ;
			
			DrawCenterString();
		}
		
		// =============================================================================
		
		/*
		* ================= SCR_CalcVrect
		* 
		* Sets scr_vrect, the coordinates of the rendered window =================
		*/
		internal static void  CalcVrect()
		{
			int size;
			
			// bound viewsize
			if (scr_viewsize.value_Renamed < 40)
				Cvar.Set("viewsize", "40");
			if (scr_viewsize.value_Renamed > 100)
				Cvar.Set("viewsize", "100");
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			size = (int) scr_viewsize.value_Renamed;
			
			scr_vrect.width = viddef.width * size / 100;
			scr_vrect.width &= ~ 7;
			
			scr_vrect.height = viddef.height * size / 100;
			scr_vrect.height &= ~ 1;
			
			scr_vrect.x = (viddef.width - scr_vrect.width) / 2;
			scr_vrect.y = (viddef.height - scr_vrect.height) / 2;
		}
		
		/*
		* ================= SCR_SizeUp_f
		* 
		* Keybinding command =================
		*/
		internal static void  SizeUp_f()
		{
			Cvar.SetValue("viewsize", scr_viewsize.value_Renamed + 10);
		}
		
		/*
		* ================= SCR_SizeDown_f
		* 
		* Keybinding command =================
		*/
		internal static void  SizeDown_f()
		{
			Cvar.SetValue("viewsize", scr_viewsize.value_Renamed - 10);
		}
		
		/*
		* ================= SCR_Sky_f
		* 
		* Set a specific sky and rotation speed =================
		*/
		internal static void  Sky_f()
		{
			float rotate;
			float[] axis = new float[]{0, 0, 0};
			
			if (Cmd.Argc() < 2)
			{
				Com.Printf("Usage: sky <basename> <rotate> <axis x y z>\n");
				return ;
			}
			if (Cmd.Argc() > 2)
				rotate = System.Single.Parse(Cmd.Argv(2));
			else
				rotate = 0;
			if (Cmd.Argc() == 6)
			{
				axis[0] = System.Single.Parse(Cmd.Argv(3));
				axis[1] = System.Single.Parse(Cmd.Argv(4));
				axis[2] = System.Single.Parse(Cmd.Argv(5));
			}
			else
			{
				axis[0] = 0;
				axis[1] = 0;
				axis[2] = 1;
			}
			
			re.SetSky(Cmd.Argv(1), rotate, axis);
		}
		
		// ============================================================================
		
		/*
		* ================== SCR_Init ==================
		*/
		internal static void  Init()
		{
			scr_viewsize = Cvar.Get("viewsize", "100", CVAR_ARCHIVE);
			scr_conspeed = Cvar.Get("scr_conspeed", "3", 0);
			scr_showturtle = Cvar.Get("scr_showturtle", "0", 0);
			scr_showpause = Cvar.Get("scr_showpause", "1", 0);
			scr_centertime = Cvar.Get("scr_centertime", "2.5", 0);
			scr_printspeed = Cvar.Get("scr_printspeed", "8", 0);
			scr_netgraph = Cvar.Get("netgraph", "1", 0);
			scr_timegraph = Cvar.Get("timegraph", "1", 0);
			scr_debuggraph = Cvar.Get("debuggraph", "1", 0);
			scr_graphheight = Cvar.Get("graphheight", "32", 0);
			scr_graphscale = Cvar.Get("graphscale", "1", 0);
			scr_graphshift = Cvar.Get("graphshift", "0", 0);
			scr_drawall = Cvar.Get("scr_drawall", "1", 0);
			fps = Cvar.Get("fps", "0", 0);
			
			//
			// register our commands
			//
			Cmd.AddCommand("timerefresh", new AnonymousClassxcommand_t());
			Cmd.AddCommand("loading", new AnonymousClassxcommand_t1());
			Cmd.AddCommand("sizeup", new AnonymousClassxcommand_t2());
			Cmd.AddCommand("sizedown", new AnonymousClassxcommand_t3());
			Cmd.AddCommand("sky", new AnonymousClassxcommand_t4());
			
			scr_initialized = true;
		}
		
		/*
		* ============== SCR_DrawNet ==============
		*/
		internal static void  DrawNet()
		{
			if (cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged < CMD_BACKUP - 1)
				return ;
			
			re.DrawPic(scr_vrect.x + 64, scr_vrect.y, "net");
		}
		
		/*
		* ============== SCR_DrawPause ==============
		*/
		internal static void  DrawPause()
		{
			System.Drawing.Size dim = new System.Drawing.Size(0, 0);
			
			if (scr_showpause.value_Renamed == 0)
			// turn off for screenshots
				return ;
			
			if (cl_paused.value_Renamed == 0)
				return ;
			
			//UPGRADE_NOTE: ref keyword was added to struct-type parameters. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1303'"
			re.DrawGetPicSize(ref dim, "pause");
			re.DrawPic((viddef.width - dim.Width) / 2, viddef.height / 2 + 8, "pause");
		}
		
		/*
		* ============== SCR_DrawLoading ==============
		*/
		internal static void  DrawLoading()
		{
			System.Drawing.Size dim = new System.Drawing.Size(0, 0);
			
			if (scr_draw_loading == 0)
				return ;
			
			scr_draw_loading = 0;
			//UPGRADE_NOTE: ref keyword was added to struct-type parameters. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1303'"
			re.DrawGetPicSize(ref dim, "loading");
			re.DrawPic((viddef.width - dim.Width) / 2, (viddef.height - dim.Height) / 2, "loading");
		}
		
		// =============================================================================
		
		/*
		* ================== SCR_RunConsole
		* 
		* Scroll it up or down ==================
		*/
		internal static void  RunConsole()
		{
			// decide on the height of the console
			if (cls.key_dest == key_console)
				scr_conlines = 0.5f;
			// half screen
			else
				scr_conlines = 0; // none visible
			
			if (scr_conlines < scr_con_current)
			{
				scr_con_current -= scr_conspeed.value_Renamed * cls.frametime;
				if (scr_conlines > scr_con_current)
					scr_con_current = scr_conlines;
			}
			else if (scr_conlines > scr_con_current)
			{
				scr_con_current += scr_conspeed.value_Renamed * cls.frametime;
				if (scr_conlines < scr_con_current)
					scr_con_current = scr_conlines;
			}
		}
		
		/*
		* ================== SCR_DrawConsole ==================
		*/
		internal static void  DrawConsole()
		{
			Console.CheckResize();
			
			if (cls.state == ca_disconnected || cls.state == ca_connecting)
			{
				// forced
				// full
				// screen
				// console
				Console.DrawConsole(1.0f);
				return ;
			}
			
			if (cls.state != ca_active || !cl.refresh_prepped)
			{
				// connected, but
				// can't render
				Console.DrawConsole(0.5f);
				re.DrawFill(0, viddef.height / 2, viddef.width, viddef.height / 2, 0);
				return ;
			}
			
			if (scr_con_current != 0)
			{
				Console.DrawConsole(scr_con_current);
			}
			else
			{
				if (cls.key_dest == key_game || cls.key_dest == key_message)
					Console.DrawNotify(); // only draw notify in game
			}
		}
		
		// =============================================================================
		
		/*
		* ================ SCR_BeginLoadingPlaque ================
		*/
		public static void  BeginLoadingPlaque()
		{
			S.StopAllSounds();
			cl.sound_prepped = false; // don't play ambients
			
			if (cls.disable_screen != 0)
				return ;
			if (developer.value_Renamed != 0)
				return ;
			if (cls.state == ca_disconnected)
				return ; // if at console, don't bring up the plaque
			if (cls.key_dest == key_console)
				return ;
			if (cl.cinematictime > 0)
				scr_draw_loading = 2;
			// clear to balack first
			else
				scr_draw_loading = 1;
			
			UpdateScreen();
			cls.disable_screen = Timer.Milliseconds();
			cls.disable_servercount = cl.servercount;
		}
		
		/*
		* ================ SCR_EndLoadingPlaque ================
		*/
		public static void  EndLoadingPlaque()
		{
			cls.disable_screen = 0;
			Console.ClearNotify();
		}
		
		/*
		* ================ SCR_Loading_f ================
		*/
		internal static void  Loading_f()
		{
			BeginLoadingPlaque();
		}
		
		/*
		* ================ SCR_TimeRefresh_f ================
		*/
		internal static void  TimeRefresh_f()
		{
			int i;
			int start, stop;
			float time;
			
			if (cls.state != ca_active)
				return ;
			
			start = Timer.Milliseconds();
			
			if (Cmd.Argc() == 2)
			{
				// run without page flipping
				re.BeginFrame(0);
				for (i = 0; i < 128; i++)
				{
					cl.refdef.viewangles[1] = i / 128.0f * 360.0f;
					re.RenderFrame(cl.refdef);
				}
				re.EndFrame();
			}
			else
			{
				for (i = 0; i < 128; i++)
				{
					cl.refdef.viewangles[1] = i / 128.0f * 360.0f;
					
					re.BeginFrame(0);
					re.RenderFrame(cl.refdef);
					re.EndFrame();
				}
			}
			
			stop = Timer.Milliseconds();
			time = (stop - start) / 1000.0f;
			Com.Printf("%f seconds (%f fps)\n", new Vargs(2).add(time).add(128.0f / time));
		}
		
		internal static void  DirtyScreen()
		{
			AddDirtyPoint(0, 0);
			AddDirtyPoint(viddef.width - 1, viddef.height - 1);
		}
		
		/*
		* ============== SCR_TileClear
		* 
		* Clear any parts of the tiled background that were drawn on last frame
		* ==============
		*/
		
		internal static dirty_t clear = new dirty_t();
		
		internal static void  TileClear()
		{
			int i;
			int top, bottom, left, right;
			clear.clear();
			
			if (scr_drawall.value_Renamed != 0)
				DirtyScreen(); // for power vr or broken page flippers...
			
			if (scr_con_current == 1.0f)
				return ; // full screen console
			if (scr_viewsize.value_Renamed == 100)
				return ; // full screen rendering
			if (cl.cinematictime > 0)
				return ; // full screen cinematic
			
			// erase rect will be the union of the past three frames
			// so tripple buffering works properly
			clear.set_Renamed(scr_dirty);
			for (i = 0; i < 2; i++)
			{
				if (scr_old_dirty[i].x1 < clear.x1)
					clear.x1 = scr_old_dirty[i].x1;
				if (scr_old_dirty[i].x2 > clear.x2)
					clear.x2 = scr_old_dirty[i].x2;
				if (scr_old_dirty[i].y1 < clear.y1)
					clear.y1 = scr_old_dirty[i].y1;
				if (scr_old_dirty[i].y2 > clear.y2)
					clear.y2 = scr_old_dirty[i].y2;
			}
			
			scr_old_dirty[1].set_Renamed(scr_old_dirty[0]);
			scr_old_dirty[0].set_Renamed(scr_dirty);
			
			scr_dirty.x1 = 9999;
			scr_dirty.x2 = - 9999;
			scr_dirty.y1 = 9999;
			scr_dirty.y2 = - 9999;
			
			// don't bother with anything convered by the console)
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			top = (int) (scr_con_current * viddef.height);
			if (top >= clear.y1)
				clear.y1 = top;
			
			if (clear.y2 <= clear.y1)
				return ; // nothing disturbed
			
			top = scr_vrect.y;
			bottom = top + scr_vrect.height - 1;
			left = scr_vrect.x;
			right = left + scr_vrect.width - 1;
			
			if (clear.y1 < top)
			{
				// clear above view screen
				i = clear.y2 < top - 1?clear.y2:top - 1;
				re.DrawTileClear(clear.x1, clear.y1, clear.x2 - clear.x1 + 1, i - clear.y1 + 1, "backtile");
				clear.y1 = top;
			}
			if (clear.y2 > bottom)
			{
				// clear below view screen
				i = clear.y1 > bottom + 1?clear.y1:bottom + 1;
				re.DrawTileClear(clear.x1, i, clear.x2 - clear.x1 + 1, clear.y2 - i + 1, "backtile");
				clear.y2 = bottom;
			}
			if (clear.x1 < left)
			{
				// clear left of view screen
				i = clear.x2 < left - 1?clear.x2:left - 1;
				re.DrawTileClear(clear.x1, clear.y1, i - clear.x1 + 1, clear.y2 - clear.y1 + 1, "backtile");
				clear.x1 = left;
			}
			if (clear.x2 > right)
			{
				// clear left of view screen
				i = clear.x1 > right + 1?clear.x1:right + 1;
				re.DrawTileClear(i, clear.y1, clear.x2 - i + 1, clear.y2 - clear.y1 + 1, "backtile");
				clear.x2 = right;
			}
		}
		
		// ===============================================================
		
		internal const int STAT_MINUS = 10; // num frame for '-' stats digit
		
		internal const int ICON_WIDTH = 24;
		
		internal const int ICON_HEIGHT = 24;
		
		internal const int CHAR_WIDTH = 16;
		
		internal const int ICON_SPACE = 8;
		
		/*
		* ================ SizeHUDString
		* 
		* Allow embedded \n in the string ================
		*/
		//UPGRADE_NOTE: ref keyword was added to struct-type parameters. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1303'"
		internal static void  SizeHUDString(System.String string_Renamed, ref System.Drawing.Size dim)
		{
			int lines, width, current;
			
			lines = 1;
			width = 0;
			
			current = 0;
			for (int i = 0; i < string_Renamed.Length; i++)
			{
				if (string_Renamed[i] == '\n')
				{
					lines++;
					current = 0;
				}
				else
				{
					current++;
					if (current > width)
						width = current;
				}
			}
			
			dim.Width = width * 8;
			dim.Height = lines * 8;
		}
		
		internal static void  DrawHUDString(System.String string_Renamed, int x, int y, int centerwidth, int xor)
		{
			int margin;
			//char line[1024];
			System.Text.StringBuilder line = new System.Text.StringBuilder(1024);
			int i;
			
			margin = x;
			
			for (int l = 0; l < string_Renamed.Length; )
			{
				// scan out one line of text from the string
				line = new System.Text.StringBuilder(1024);
				while (l < string_Renamed.Length && string_Renamed[l] != '\n')
				{
					line.Append(string_Renamed[l]);
					l++;
				}
				
				if (centerwidth != 0)
					x = margin + (centerwidth - line.Length * 8) / 2;
				else
					x = margin;
				for (i = 0; i < line.Length; i++)
				{
					re.DrawChar(x, y, line[i] ^ xor);
					x += 8;
				}
				if (l < string_Renamed.Length)
				{
					l++; // skip the \n
					x = margin;
					y += 8;
				}
			}
		}
		
		/*
		* ============== SCR_DrawField ==============
		*/
		internal static void  DrawField(int x, int y, int color, int width, int value_Renamed)
		{
			char ptr;
			System.String num;
			int l;
			int frame;
			
			if (width < 1)
				return ;
			
			// draw number string
			if (width > 5)
				width = 5;
			
			AddDirtyPoint(x, y);
			AddDirtyPoint(x + width * CHAR_WIDTH + 2, y + 23);
			
			num = "" + value_Renamed;
			l = num.Length;
			if (l > width)
				l = width;
			x += 2 + CHAR_WIDTH * (width - l);
			
			ptr = num[0];
			
			for (int i = 0; i < l; i++)
			{
				ptr = num[i];
				if (ptr == '-')
					frame = STAT_MINUS;
				else
					frame = ptr - '0';
				
				re.DrawPic(x, y, sb_nums[color][frame]);
				x += CHAR_WIDTH;
			}
		}
		
		/*
		* =============== SCR_TouchPics
		* 
		* Allows rendering code to cache all needed sbar graphics ===============
		*/
		internal static void  TouchPics()
		{
			int i, j;
			
			for (i = 0; i < 2; i++)
				for (j = 0; j < 11; j++)
					re.RegisterPic(sb_nums[i][j]);
			
			if (crosshair.value_Renamed != 0.0f)
			{
				if (crosshair.value_Renamed > 3.0f || crosshair.value_Renamed < 0.0f)
					crosshair.value_Renamed = 3.0f;
				
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				crosshair_pic = "ch" + (int) crosshair.value_Renamed;
				System.Drawing.Size dim = new System.Drawing.Size(0, 0);
				//UPGRADE_NOTE: ref keyword was added to struct-type parameters. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1303'"
				re.DrawGetPicSize(ref dim, crosshair_pic);
				crosshair_width = dim.Width;
				crosshair_height = dim.Height;
				if (crosshair_width == 0)
					crosshair_pic = "";
			}
		}
		
		/*
		* ================ SCR_ExecuteLayoutString
		* 
		* ================
		*/
		internal static void  ExecuteLayoutString(System.String s)
		{
			int x, y;
			int value_Renamed;
			System.String token;
			int width;
			int index;
			clientinfo_t ci;
			
			if (cls.state != ca_active || !cl.refresh_prepped)
				return ;
			
			//		if (!s[0])
			if (s == null || s.Length == 0)
				return ;
			
			x = 0;
			y = 0;
			width = 3;
			
			Com.ParseHelp ph = new Com.ParseHelp(s);
			
			while (!ph.Eof)
			{
				token = Com.Parse(ph);
				if (token.Equals("xl"))
				{
					token = Com.Parse(ph);
					x = Lib.atoi(token);
					continue;
				}
				if (token.Equals("xr"))
				{
					token = Com.Parse(ph);
					x = viddef.width + Lib.atoi(token);
					continue;
				}
				if (token.Equals("xv"))
				{
					token = Com.Parse(ph);
					x = viddef.width / 2 - 160 + Lib.atoi(token);
					continue;
				}
				
				if (token.Equals("yt"))
				{
					token = Com.Parse(ph);
					y = Lib.atoi(token);
					continue;
				}
				if (token.Equals("yb"))
				{
					token = Com.Parse(ph);
					y = viddef.height + Lib.atoi(token);
					continue;
				}
				if (token.Equals("yv"))
				{
					token = Com.Parse(ph);
					y = viddef.height / 2 - 120 + Lib.atoi(token);
					continue;
				}
				
				if (token.Equals("pic"))
				{
					// draw a pic from a stat number
					token = Com.Parse(ph);
					value_Renamed = cl.frame.playerstate.stats[Lib.atoi(token)];
					if (value_Renamed >= MAX_IMAGES)
						Com.Error(ERR_DROP, "Pic >= MAX_IMAGES");
					if (cl.configstrings[CS_IMAGES + value_Renamed] != null)
					{
						AddDirtyPoint(x, y);
						AddDirtyPoint(x + 23, y + 23);
						re.DrawPic(x, y, cl.configstrings[CS_IMAGES + value_Renamed]);
					}
					continue;
				}
				
				if (token.Equals("client"))
				{
					// draw a deathmatch client block
					int score, ping, time;
					
					token = Com.Parse(ph);
					x = viddef.width / 2 - 160 + Lib.atoi(token);
					token = Com.Parse(ph);
					y = viddef.height / 2 - 120 + Lib.atoi(token);
					AddDirtyPoint(x, y);
					AddDirtyPoint(x + 159, y + 31);
					
					token = Com.Parse(ph);
					value_Renamed = Lib.atoi(token);
					if (value_Renamed >= MAX_CLIENTS || value_Renamed < 0)
						Com.Error(ERR_DROP, "client >= MAX_CLIENTS");
					ci = cl.clientinfo[value_Renamed];
					
					token = Com.Parse(ph);
					score = Lib.atoi(token);
					
					token = Com.Parse(ph);
					ping = Lib.atoi(token);
					
					token = Com.Parse(ph);
					time = Lib.atoi(token);
					
					Console.DrawAltString(x + 32, y, ci.name);
					Console.DrawString(x + 32, y + 8, "Score: ");
					Console.DrawAltString(x + 32 + 7 * 8, y + 8, "" + score);
					Console.DrawString(x + 32, y + 16, "Ping:  " + ping);
					Console.DrawString(x + 32, y + 24, "Time:  " + time);
					
					if (ci.icon == null)
						ci = cl.baseclientinfo;
					re.DrawPic(x, y, ci.iconname);
					continue;
				}
				
				if (token.Equals("ctf"))
				{
					// draw a ctf client block
					int score, ping;
					
					token = Com.Parse(ph);
					x = viddef.width / 2 - 160 + Lib.atoi(token);
					token = Com.Parse(ph);
					y = viddef.height / 2 - 120 + Lib.atoi(token);
					AddDirtyPoint(x, y);
					AddDirtyPoint(x + 159, y + 31);
					
					token = Com.Parse(ph);
					value_Renamed = Lib.atoi(token);
					if (value_Renamed >= MAX_CLIENTS || value_Renamed < 0)
						Com.Error(ERR_DROP, "client >= MAX_CLIENTS");
					ci = cl.clientinfo[value_Renamed];
					
					token = Com.Parse(ph);
					score = Lib.atoi(token);
					
					token = Com.Parse(ph);
					ping = Lib.atoi(token);
					if (ping > 999)
						ping = 999;
					
					// sprintf(block, "%3d %3d %-12.12s", score, ping, ci->name);
					System.String block = Com.sprintf("%3d %3d %-12.12s", new Vargs(3).add(score).add(ping).add(ci.name));
					
					if (value_Renamed == cl.playernum)
						Console.DrawAltString(x, y, block);
					else
						Console.DrawString(x, y, block);
					continue;
				}
				
				if (token.Equals("picn"))
				{
					// draw a pic from a name
					token = Com.Parse(ph);
					AddDirtyPoint(x, y);
					AddDirtyPoint(x + 23, y + 23);
					re.DrawPic(x, y, token);
					continue;
				}
				
				if (token.Equals("num"))
				{
					// draw a number
					token = Com.Parse(ph);
					width = Lib.atoi(token);
					token = Com.Parse(ph);
					value_Renamed = cl.frame.playerstate.stats[Lib.atoi(token)];
					DrawField(x, y, 0, width, value_Renamed);
					continue;
				}
				
				if (token.Equals("hnum"))
				{
					// health number
					int color;
					
					width = 3;
					value_Renamed = cl.frame.playerstate.stats[STAT_HEALTH];
					if (value_Renamed > 25)
						color = 0;
					// green
					else if (value_Renamed > 0)
						color = (cl.frame.serverframe >> 2) & 1;
					// flash
					else
						color = 1;
					
					if ((cl.frame.playerstate.stats[STAT_FLASHES] & 1) != 0)
						re.DrawPic(x, y, "field_3");
					
					DrawField(x, y, color, width, value_Renamed);
					continue;
				}
				
				if (token.Equals("anum"))
				{
					// ammo number
					int color;
					
					width = 3;
					value_Renamed = cl.frame.playerstate.stats[STAT_AMMO];
					if (value_Renamed > 5)
						color = 0;
					// green
					else if (value_Renamed >= 0)
						color = (cl.frame.serverframe >> 2) & 1;
					// flash
					else
						continue; // negative number = don't show
					
					if ((cl.frame.playerstate.stats[STAT_FLASHES] & 4) != 0)
						re.DrawPic(x, y, "field_3");
					
					DrawField(x, y, color, width, value_Renamed);
					continue;
				}
				
				if (token.Equals("rnum"))
				{
					// armor number
					int color;
					
					width = 3;
					value_Renamed = cl.frame.playerstate.stats[STAT_ARMOR];
					if (value_Renamed < 1)
						continue;
					
					color = 0; // green
					
					if ((cl.frame.playerstate.stats[STAT_FLASHES] & 2) != 0)
						re.DrawPic(x, y, "field_3");
					
					DrawField(x, y, color, width, value_Renamed);
					continue;
				}
				
				if (token.Equals("stat_string"))
				{
					token = Com.Parse(ph);
					index = Lib.atoi(token);
					if (index < 0 || index >= MAX_CONFIGSTRINGS)
						Com.Error(ERR_DROP, "Bad stat_string index");
					index = cl.frame.playerstate.stats[index];
					if (index < 0 || index >= MAX_CONFIGSTRINGS)
						Com.Error(ERR_DROP, "Bad stat_string index");
					Console.DrawString(x, y, cl.configstrings[index]);
					continue;
				}
				
				if (token.Equals("cstring"))
				{
					token = Com.Parse(ph);
					DrawHUDString(token, x, y, 320, 0);
					continue;
				}
				
				if (token.Equals("string"))
				{
					token = Com.Parse(ph);
					Console.DrawString(x, y, token);
					continue;
				}
				
				if (token.Equals("cstring2"))
				{
					token = Com.Parse(ph);
					DrawHUDString(token, x, y, 320, 0x80);
					continue;
				}
				
				if (token.Equals("string2"))
				{
					token = Com.Parse(ph);
					Console.DrawAltString(x, y, token);
					continue;
				}
				
				if (token.Equals("if"))
				{
					// draw a number
					token = Com.Parse(ph);
					value_Renamed = cl.frame.playerstate.stats[Lib.atoi(token)];
					if (value_Renamed == 0)
					{
						// skip to endif
						while (!ph.Eof && !(token = Com.Parse(ph)).Equals("endif"))
							;
					}
					continue;
				}
			}
		}
		
		/*
		* ================ SCR_DrawStats
		* 
		* The status bar is a small layout program that is based on the stats array
		* ================
		*/
		internal static void  DrawStats()
		{
			//TODO:
			SCR.ExecuteLayoutString(cl.configstrings[CS_STATUSBAR]);
		}
		
		/*
		* ================ SCR_DrawLayout
		* 
		* ================
		*/
		new internal const int STAT_LAYOUTS = 13;
		
		internal static void  DrawLayout()
		{
			if (cl.frame.playerstate.stats[STAT_LAYOUTS] != 0)
				SCR.ExecuteLayoutString(cl.layout);
		}
		
		// =======================================================
		
		/*
		* ================== SCR_UpdateScreen
		* 
		* This is called every frame, and can also be called explicitly to flush
		* text to the screen. ==================
		*/
		//UPGRADE_NOTE: Final was removed from the declaration of 'separation'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly float[] separation = new float[]{0, 0};
		
		internal static void  UpdateScreen2()
		{
			int numframes;
			int i;
			// if the screen is disabled (loading plaque is up, or vid mode
			// changing)
			// do nothing at all
			if (cls.disable_screen != 0)
			{
				if (Timer.Milliseconds() - cls.disable_screen > 120000)
				{
					cls.disable_screen = 0;
					Com.Printf("Loading plaque timed out.\n");
				}
				return ;
			}
			
			if (!scr_initialized || !con.initialized)
				return ; // not initialized yet
			
			/*
			* * range check cl_camera_separation so we don't inadvertently fry
			* someone's * brain
			*/
			if (cl_stereo_separation.value_Renamed > 1.0)
				Cvar.SetValue("cl_stereo_separation", 1.0f);
			else if (cl_stereo_separation.value_Renamed < 0)
				Cvar.SetValue("cl_stereo_separation", 0.0f);
			
			if (cl_stereo.value_Renamed != 0)
			{
				numframes = 2;
				separation[0] = (- cl_stereo_separation.value_Renamed) / 2;
				separation[1] = cl_stereo_separation.value_Renamed / 2;
			}
			else
			{
				separation[0] = 0;
				separation[1] = 0;
				numframes = 1;
			}
			
			for (i = 0; i < numframes; i++)
			{
				re.BeginFrame(separation[i]);
				
				if (scr_draw_loading == 2)
				{
					//  loading plaque over black screen
					System.Drawing.Size dim = new System.Drawing.Size(0, 0);
					
					re.CinematicSetPalette(null);
					scr_draw_loading = 0; // false
					//UPGRADE_NOTE: ref keyword was added to struct-type parameters. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1303'"
					re.DrawGetPicSize(ref dim, "loading");
					re.DrawPic((viddef.width - dim.Width) / 2, (viddef.height - dim.Height) / 2, "loading");
				}
				// if a cinematic is supposed to be running, handle menus
				// and console specially
				else if (cl.cinematictime > 0)
				{
					if (cls.key_dest == key_menu)
					{
						if (cl.cinematicpalette_active)
						{
							re.CinematicSetPalette(null);
							cl.cinematicpalette_active = false;
						}
						Menu.Draw();
					}
					else if (cls.key_dest == key_console)
					{
						if (cl.cinematicpalette_active)
						{
							re.CinematicSetPalette(null);
							cl.cinematicpalette_active = false;
						}
						DrawConsole();
					}
					else
					{
						// TODO implement cinematics completely
						DrawCinematic();
					}
				}
				else
				{
					// make sure the game palette is active
					if (cl.cinematicpalette_active)
					{
						re.CinematicSetPalette(null);
						cl.cinematicpalette_active = false;
					}
					
					// do 3D refresh drawing, and then update the screen
					CalcVrect();
					
					// clear any dirty part of the background
					TileClear();
					
					V.RenderView(separation[i]);
					
					DrawStats();
					
					if ((cl.frame.playerstate.stats[STAT_LAYOUTS] & 1) != 0)
						DrawLayout();
					if ((cl.frame.playerstate.stats[STAT_LAYOUTS] & 2) != 0)
						CL_inv.DrawInventory();
					
					DrawNet();
					CheckDrawCenterString();
					DrawFPS();
					
					//
					//				if (scr_timegraph->value)
					//					SCR_DebugGraph (cls.frametime*300, 0);
					//
					//				if (scr_debuggraph->value || scr_timegraph->value ||
					// scr_netgraph->value)
					//					SCR_DrawDebugGraph ();
					//
					DrawPause();
					DrawConsole();
					Menu.Draw();
					DrawLoading();
				}
			}
			
			Globals.re.EndFrame();
		}
		
		/*
		* ================= SCR_DrawCrosshair =================
		*/
		internal static void  DrawCrosshair()
		{
			if (crosshair.value_Renamed == 0.0f)
				return ;
			
			if (crosshair.modified)
			{
				crosshair.modified = false;
				SCR.TouchPics();
			}
			
			if (crosshair_pic.Length == 0)
				return ;
			
			re.DrawPic(scr_vrect.x + ((scr_vrect.width - crosshair_width) >> 1), scr_vrect.y + ((scr_vrect.height - crosshair_height) >> 1), crosshair_pic);
		}
		
		//UPGRADE_NOTE: The initialization of  'updateScreenCallback' was moved to static method 'jake2.client.SCR'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'"
		private static xcommand_t updateScreenCallback;
		
		// wird anstelle von der richtigen UpdateScreen benoetigt
		public static void  UpdateScreen()
		{
			Globals.re.updateScreen(updateScreenCallback);
		}
		
		/*
		* ================= SCR_AddDirtyPoint =================
		*/
		internal static void  AddDirtyPoint(int x, int y)
		{
			if (x < scr_dirty.x1)
				scr_dirty.x1 = x;
			if (x > scr_dirty.x2)
				scr_dirty.x2 = x;
			if (y < scr_dirty.y1)
				scr_dirty.y1 = y;
			if (y > scr_dirty.y2)
				scr_dirty.y2 = y;
		}
		
		private static int lastframes = 0;
		
		private static int lasttime = 0;
		
		private static System.String fpsvalue = "";
		
		internal static void  DrawFPS()
		{
			if (fps.value_Renamed > 0.0f)
			{
				if (fps.modified)
				{
					fps.modified = false;
					Cvar.SetValue("cl_maxfps", 1000);
				}
				
				int diff = cls.realtime - lasttime;
				//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
				if (diff > (int) (fps.value_Renamed * 1000))
				{
					fpsvalue = (cls.framecount - lastframes) * 100000 / diff / 100.0f + " fps";
					lastframes = cls.framecount;
					lasttime = cls.realtime;
				}
				int x = viddef.width - 8 * fpsvalue.Length - 2;
				for (int i = 0; i < fpsvalue.Length; i++)
				{
					re.DrawChar(x, 2, fpsvalue[i]);
					x += 8;
				}
			}
			else if (fps.modified)
			{
				fps.modified = false;
				Cvar.SetValue("cl_maxfps", 90);
			}
		}
		
		/*
		* =================================================================
		* 
		* cl_cin.c
		* 
		* Play Cinematics
		* 
		* =================================================================
		*/
		
		//UPGRADE_NOTE: The access modifier for this class or class field has been changed in order to prevent compilation errors due to the visibility level. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1296'"
		internal class cinematics_t
		{
			internal bool restart_sound;
			internal int s_rate;
			internal int s_width;
			internal int s_channels;
			
			internal int width;
			internal int height;
			internal sbyte[] pic;
			internal sbyte[] pic_pending;
			// order 1 huffman stuff
			internal int[] hnodes1; // [256][256][2];
			internal int[] numhnodes1 = new int[256];
			
			internal int[] h_used = new int[512];
			internal int[] h_count = new int[512];
		}
		
		private static cinematics_t cin = new cinematics_t();
		
		/// <summary> LoadPCX</summary>
		internal static int LoadPCX(System.String filename, sbyte[] palette, cinematics_t cin)
		{
			qfiles.pcx_t pcx;
			
			// load the file
			ByteBuffer raw = FS.LoadMappedFile(filename);
			
			if (raw == null)
			{
				VID.Printf(Defines.PRINT_DEVELOPER, "Bad pcx file " + filename + '\n');
				return 0;
			}
			
			// parse the PCX file
			pcx = new qfiles.pcx_t(raw);
			
			if (pcx.manufacturer != 0x0a || pcx.version != 5 || pcx.encoding != 1 || pcx.bits_per_pixel != 8 || pcx.xmax >= 640 || pcx.ymax >= 480)
			{
				
				VID.Printf(Defines.PRINT_ALL, "Bad pcx file " + filename + '\n');
				return 0;
			}
			
			int width = pcx.xmax - pcx.xmin + 1;
			int height = pcx.ymax - pcx.ymin + 1;
			
			sbyte[] pix = new sbyte[width * height];
			
			if (palette != null)
			{
				raw.position(raw.limit() - 768);
				raw.get_Renamed(palette);
			}
			
			if (cin != null)
			{
				cin.pic = pix;
				cin.width = width;
				cin.height = height;
			}
			
			//
			// decode pcx
			//
			int count = 0;
			sbyte dataByte = 0;
			int runLength = 0;
			int x, y;
			
			// simple counter for buffer indexing
			int p = 0;
			
			for (y = 0; y < height; y++)
			{
				for (x = 0; x < width; )
				{
					
					dataByte = pcx.data.get_Renamed(p++);
					
					if ((dataByte & 0xC0) == 0xC0)
					{
						runLength = dataByte & 0x3F;
						dataByte = pcx.data.get_Renamed(p++);
						// write runLength pixel
						while (runLength-- > 0)
						{
							pix[count++] = dataByte;
							x++;
						}
					}
					else
					{
						// write one pixel
						pix[count++] = dataByte;
						x++;
					}
				}
			}
			return width * height;
		}
		
		/// <summary> StopCinematic</summary>
		internal static void  StopCinematic()
		{
			if (cin.restart_sound)
			{
				// done
				cl.cinematictime = 0;
				cin.pic = null;
				cin.pic_pending = null;
				if (cl.cinematicpalette_active)
				{
					re.CinematicSetPalette(null);
					cl.cinematicpalette_active = false;
				}
				if (cl.cinematic_file != null)
				{
					// free the mapped byte buffer
					cl.cinematic_file = null;
				}
				if (cin.hnodes1 != null)
				{
					cin.hnodes1 = null;
				}
				
				S.disableStreaming();
				cin.restart_sound = false;
			}
		}
		
		/// <summary> FinishCinematic
		/// 
		/// Called when either the cinematic completes, or it is aborted
		/// </summary>
		internal static void  FinishCinematic()
		{
			// tell the server to advance to the next map / cinematic
			MSG.WriteByte(cls.netchan.message, clc_stringcmd);
			SZ.Print(cls.netchan.message, "nextserver " + cl.servercount + '\n');
		}
		
		// ==========================================================================
		
		/// <summary> SmallestNode1
		/// 
		/// </summary>
		private static int SmallestNode1(int numhnodes)
		{
			
			int best = 99999999;
			int bestnode = - 1;
			for (int i = 0; i < numhnodes; i++)
			{
				if (cin.h_used[i] != 0)
					continue;
				if (cin.h_count[i] == 0)
					continue;
				if (cin.h_count[i] < best)
				{
					best = cin.h_count[i];
					bestnode = i;
				}
			}
			
			if (bestnode == - 1)
				return - 1;
			
			cin.h_used[bestnode] = 1; // true
			return bestnode;
		}
		
		
		/// <summary> Huff1TableInit
		/// 
		/// Reads the 64k counts table and initializes the node trees.
		/// 
		/// </summary>
		private static void  Huff1TableInit()
		{
			int[] node;
			sbyte[] counts = new sbyte[256];
			int numhnodes;
			
			cin.hnodes1 = new int[256 * 256 * 2];
			SupportClass.ArraySupport.Fill(cin.hnodes1, 0);
			
			for (int prev = 0; prev < 256; prev++)
			{
				SupportClass.ArraySupport.Fill(cin.h_count, 0);
				SupportClass.ArraySupport.Fill(cin.h_used, 0);
				
				// read a row of counts
				cl.cinematic_file.get_Renamed(counts);
				for (int j = 0; j < 256; j++)
					cin.h_count[j] = counts[j] & 0xFF;
				
				// build the nodes
				numhnodes = 256;
				int nodebase = 0 + prev * 256 * 2;
				int index = 0;
				node = cin.hnodes1;
				while (numhnodes != 511)
				{
					index = nodebase + (numhnodes - 256) * 2;
					
					// pick two lowest counts
					node[index] = SmallestNode1(numhnodes);
					if (node[index] == - 1)
						break; // no more
					
					node[index + 1] = SmallestNode1(numhnodes);
					if (node[index + 1] == - 1)
						break;
					
					cin.h_count[numhnodes] = cin.h_count[node[index]] + cin.h_count[node[index + 1]];
					numhnodes++;
				}
				
				cin.numhnodes1[prev] = numhnodes - 1;
			}
		}
		
		/// <summary> Huff1Decompress
		/// 
		/// </summary>
		private static sbyte[] Huff1Decompress(sbyte[] in_Renamed, int size)
		{
			// get decompressed count
			int count = (in_Renamed[0] & 0xFF) | ((in_Renamed[1] & 0xFF) << 8) | ((in_Renamed[2] & 0xFF) << 16) | ((in_Renamed[3] & 0xFF) << 24);
			// used as index for in[];
			int input = 4;
			sbyte[] out_Renamed = new sbyte[count];
			// used as index for out[];
			int out_p = 0;
			
			// read bits
			
			int hnodesbase = (- 256) * 2; // nodes 0-255 aren't stored
			int index = hnodesbase;
			int[] hnodes = cin.hnodes1;
			int nodenum = cin.numhnodes1[0];
			int inbyte;
			while (count != 0)
			{
				inbyte = in_Renamed[input++] & 0xFF;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
				
				if (nodenum < 256)
				{
					index = hnodesbase + (nodenum << 9);
					out_Renamed[out_p++] = (sbyte) nodenum;
					if (--count == 0)
						break;
					nodenum = cin.numhnodes1[nodenum];
				}
				nodenum = hnodes[index + nodenum * 2 + (inbyte & 1)];
				inbyte >>= 1;
			}
			
			if (input != size && input != size + 1)
			{
				Com.Printf("Decompression overread by " + (input - size));
			}
			
			return out_Renamed;
		}
		
		private static sbyte[] compressed = new sbyte[0x20000];
		
		/// <summary> ReadNextFrame</summary>
		internal static sbyte[] ReadNextFrame()
		{
			
			ByteBuffer file = cl.cinematic_file;
			
			// read the next frame
			int command = file.getInt();
			
			if (command == 2)
			{
				// last frame marker
				return null;
			}
			
			if (command == 1)
			{
				// read palette
				file.get_Renamed(cl.cinematicpalette);
				// dubious.... exposes an edge case
				cl.cinematicpalette_active = false;
			}
			// decompress the next frame
			int size = file.getInt();
			if (size > compressed.Length || size < 1)
				Com.Error(ERR_DROP, "Bad compressed frame size:" + size);
			
			file.get_Renamed(compressed, 0, size);
			
			// read sound
			int start = cl.cinematicframe * cin.s_rate / 14;
			int end = (cl.cinematicframe + 1) * cin.s_rate / 14;
			int count = end - start;
			
			S.RawSamples(count, cin.s_rate, cin.s_width, cin.s_channels, file.slice());
			// skip the sound samples
			file.position(file.position() + count * cin.s_width * cin.s_channels);
			
			sbyte[] pic = Huff1Decompress(compressed, size);
			cl.cinematicframe++;
			
			return pic;
		}
		
		/// <summary> RunCinematic</summary>
		internal static void  RunCinematic()
		{
			if (cl.cinematictime <= 0)
			{
				StopCinematic();
				return ;
			}
			
			if (cl.cinematicframe == - 1)
			{
				// static image
				return ;
			}
			
			if (cls.key_dest != key_game)
			{
				// pause if menu or console is up
				cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
				return ;
			}
			
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			int frame = (int) ((cls.realtime - cl.cinematictime) * 14.0f / 1000);
			
			if (frame <= cl.cinematicframe)
				return ;
			
			if (frame > cl.cinematicframe + 1)
			{
				Com.Println("Dropped frame: " + frame + " > " + (cl.cinematicframe + 1));
				cl.cinematictime = cls.realtime - cl.cinematicframe * 1000 / 14;
			}
			
			cin.pic = cin.pic_pending;
			cin.pic_pending = ReadNextFrame();
			
			if (cin.pic_pending == null)
			{
				StopCinematic();
				FinishCinematic();
				// hack to get the black screen behind loading
				cl.cinematictime = 1;
				BeginLoadingPlaque();
				cl.cinematictime = 0;
				return ;
			}
		}
		
		/// <summary> DrawCinematic
		/// 
		/// Returns true if a cinematic is active, meaning the view rendering should
		/// be skipped.
		/// </summary>
		internal static bool DrawCinematic()
		{
			if (cl.cinematictime <= 0)
			{
				return false;
			}
			
			if (cls.key_dest == key_menu)
			{
				// blank screen and pause if menu is up
				Globals.re.CinematicSetPalette(null);
				cl.cinematicpalette_active = false;
				return true;
			}
			
			if (!cl.cinematicpalette_active)
			{
				re.CinematicSetPalette(cl.cinematicpalette);
				cl.cinematicpalette_active = true;
			}
			
			if (cin.pic == null)
				return true;
			
			Globals.re.DrawStretchRaw(0, 0, viddef.width, viddef.height, cin.width, cin.height, cin.pic);
			
			return true;
		}
		
		/// <summary> PlayCinematic</summary>
		internal static void  PlayCinematic(System.String arg)
		{
			
			// make sure CD isn't playing music
			//CDAudio.Stop();
			
			cl.cinematicframe = 0;
			if (arg.EndsWith(".pcx"))
			{
				// static pcx image
				System.String name = "pics/" + arg;
				int size = LoadPCX(name, cl.cinematicpalette, cin);
				cl.cinematicframe = - 1;
				cl.cinematictime = 1;
				EndLoadingPlaque();
				cls.state = ca_active;
				if (size == 0 || cin.pic == null)
				{
					Com.Println(name + " not found.");
					cl.cinematictime = 0;
				}
				return ;
			}
			
			System.String name2 = "video/" + arg;
			cl.cinematic_file = FS.LoadMappedFile(name2);
			if (cl.cinematic_file == null)
			{
				//Com.Error(ERR_DROP, "Cinematic " + name + " not found.\n");
				FinishCinematic();
				// done
				cl.cinematictime = 0;
				return ;
			}
			
			EndLoadingPlaque();
			
			cls.state = ca_active;
			
			cl.cinematic_file.order(ByteOrder.LITTLE_ENDIAN);
			ByteBuffer file = cl.cinematic_file;
			cin.width = file.getInt();
			cin.height = file.getInt();
			cin.s_rate = file.getInt();
			cin.s_width = file.getInt();
			cin.s_channels = file.getInt();
			
			Huff1TableInit();
			
			cin.restart_sound = true;
			cl.cinematicframe = 0;
			cin.pic = ReadNextFrame();
			cl.cinematictime = Timer.Milliseconds();
		}
		static SCR()
		{
			{
				for (int n = 0; n < 1024; n++)
					values[n] = new graphsamp_t();
			}
			updateScreenCallback = new AnonymousClassxcommand_t5();
		}
	}
}